home *** CD-ROM | disk | FTP | other *** search
/ Clickx 47 / Clickx 47.iso / assets / software / Miro_Installer.exe / xulrunner / python / download_utils.py < prev    next >
Encoding:
Python Source  |  2008-01-10  |  7.0 KB  |  228 lines

  1. # Miro - an RSS based video player application
  2. # Copyright (C) 2005-2007 Participatory Culture Foundation
  3. #
  4. # This program is free software; you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation; either version 2 of the License, or
  7. # (at your option) any later version.
  8. #
  9. # This program is distributed in the hope that it will be useful,
  10. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. # GNU General Public License for more details.
  13. #
  14. # You should have received a copy of the GNU General Public License
  15. # along with this program; if not, write to the Free Software
  16. # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
  17.  
  18. from os import access, F_OK
  19. from urlparse import urlparse
  20. import os.path
  21. import re
  22. import urllib
  23. import filetypes
  24. import util
  25.  
  26. from util import checkF, checkU, returnsFilename
  27. from platformutils import unicodeToFilename, unmakeURLSafe
  28.  
  29. URIPattern = re.compile(r'^([^?]*/)?([^/?]*)/*(\?(.*))?$')
  30. # filename limits this is mostly for windows where we have a 255 character
  31. # limit on entire pathname
  32. MAX_FILENAME_LENGTH = 100 
  33. MAX_FILENAME_EXTENSION_LENGTH = 50
  34.  
  35. def fixFileURLS(url):
  36.     """Fix file URLS that start with file:// instead of file:///.  Note: this
  37.     breaks for file URLS that include a hostname, but we never use those and
  38.     it's not so clear what that would mean anyway -- file URLs is an ad-hoc
  39.     spec as I can tell.."""
  40.     if url.startswith('file://'):
  41.         if not url.startswith('file:///'):
  42.             url = 'file:///%s' % url[len('file://'):]
  43.         url = url.replace('\\', '/')
  44.     return url
  45.  
  46. def defaultPort(scheme):
  47.     if scheme == 'https':
  48.         return 443
  49.     elif scheme == 'http':
  50.         return 80
  51.     elif scheme == 'file':
  52.         return None
  53.     else:
  54.         if util.chatter:
  55.             print "WARNING: Assuming port 80 for scheme: %s" % scheme
  56.         return 80
  57.  
  58. def parseURL(url, split_path=False):
  59.     url = fixFileURLS(url)
  60.     (scheme, host, path, params, query, fragment) = util.unicodify(list(urlparse(url)))
  61.     # Filter invalid URLs with duplicated ports (http://foo.bar:123:123/baz)
  62.     # which seem to be part of #441.
  63.     if host.count(':') > 1:
  64.         host = host[0:host.rfind(':')]
  65.  
  66.     if scheme == '' and util.chatter:
  67.         print "WARNING: %r has no scheme" % url
  68.  
  69.     if ':' in host:
  70.         host, port = host.split(':')
  71.         try:
  72.             port = int(port)
  73.         except:
  74.             print "DTV: parseURL: WARNING: invalid port for %r" % url
  75.             port = defaultPort(scheme)
  76.     else:
  77.         port = defaultPort(scheme)
  78.  
  79.     host = host.lower()
  80.     scheme = scheme.lower()
  81.  
  82.     path = path.replace('|', ':') 
  83.     # Windows drive names are often specified as "C|\foo\bar"
  84.  
  85.     if path == '' or path[0] != '/':
  86.         path = '/' + path
  87.     elif re.match(r'/[a-zA-Z]:', path):
  88.         # Fix "/C:/foo" paths
  89.         path = path[1:]
  90.     fullPath = path
  91.     if split_path:
  92.         return scheme, host, port, fullPath, params, query
  93.     else:
  94.         if params:
  95.             fullPath += ';%s' % params
  96.         if query:
  97.             fullPath += '?%s' % query
  98.         return scheme, host, port, fullPath
  99.  
  100. def getFileURLPath(url):
  101.     scheme, host, port, path = parseURL(url)
  102.     if scheme != 'file':
  103.         raise ValueError("%r is not a file URL" % url)
  104.     return unmakeURLSafe(path)
  105.  
  106. # If a filename doesn't have an extension, this tries to find a suitable one
  107. # based on the HTTP content-type info and add it if one is available.
  108. def checkFilenameExtension(filename, httpInfo):
  109.     checkF(filename)
  110.     if 'content-type' in httpInfo and not filetypes.isAllowedFilename(filename):
  111.         guessedExt = filetypes.guessExtension(httpInfo['content-type'])
  112.         if guessedExt is not None:
  113.             filename += guessedExt
  114.     return filename
  115.  
  116. ##
  117. # Finds a filename that's unused and similar the the file we want
  118. # to download
  119. @returnsFilename
  120. def nextFreeFilename(name):
  121.     checkF(name)
  122.     if not access(name,F_OK):
  123.         return name
  124.     parts = name.split('.')
  125.     count = 1
  126.     if len(parts) == 1:
  127.         newname = "%s.%s" % (name, count)
  128.         while access(newname,F_OK):
  129.             count += 1
  130.             newname = "%s.%s" % (name, count)
  131.     else:
  132.         parts[-1:-1] = [str(count)]
  133.         newname = '.'.join(parts)
  134.         while access(newname,F_OK):
  135.             count += 1
  136.             parts[-2] = str(count)
  137.             newname = '.'.join(parts)
  138.     return newname
  139.  
  140. ##
  141. # Returns a reasonable filename for saving the given url
  142. @returnsFilename
  143. def filenameFromURL(url, clean=False):
  144.     checkU(url)
  145.     try:
  146.         match = URIPattern.match(url)
  147.         if match is None:
  148.             # This code path will never be executed.
  149.             return unicodeToFilename(url)
  150.         filename = match.group(2)
  151.         query = match.group(4)
  152.         if not filename:
  153.             ret = query
  154.         elif not query:
  155.             ret = filename
  156.         else:
  157.             root, ext = os.path.splitext(filename)
  158.             ret = u"%s-%s%s" % (root, query, ext)
  159.         if ret is None:
  160.             ret = u'unknown'
  161.         if clean:
  162.             return cleanFilename(ret)
  163.         else:
  164.             return unicodeToFilename(ret)
  165.     except:
  166.         return unicodeToFilename(u'unknown')
  167.  
  168. # Given either a filename or a unicode "filename" return a valid clean
  169. # version of it
  170. @returnsFilename
  171. def cleanFilename(filename):
  172.     for char in ':?><|*/\\"\'':
  173.         filename = filename.replace(char,'')
  174.     if len(filename) == 0:
  175.         return unicodeToFilename(u'_')
  176.     if len(filename) > MAX_FILENAME_LENGTH:
  177.         base, ext = os.path.splitext(filename)
  178.         ext = ext[:MAX_FILENAME_EXTENSION_LENGTH]
  179.         base = base[:MAX_FILENAME_LENGTH-len(ext)]
  180.         filename = base + ext
  181.     if type(filename) == str:
  182.         return unicodeToFilename(filename.decode('ascii', 'replace'))
  183.     else:
  184.         return unicodeToFilename(filename)
  185.  
  186. # Saves data, returns filename, doesn't write over existing files.
  187. def saveData (target, suggested_basename, data):
  188.     try:
  189.         os.makedirs(target)
  190.     except:
  191.         pass
  192.  
  193.     filename = os.path.join (target, suggested_basename)
  194.  
  195.     try:
  196.         # Write to a temp file.
  197.         tmp_filename = filename + ".part"
  198. #        tmp_filename = shortenFilename (tmp_filename)
  199.         tmp_filename = nextFreeFilename (tmp_filename)
  200.         output = file (tmp_filename, 'wb')
  201.         output.write(data)
  202.         output.close()
  203.     except IOError:
  204.         try:
  205.             os.remove (tmp_filename)
  206.         except:
  207.             pass
  208.         raise
  209.  
  210. #    filename = shortenFilename (filename)
  211.     filename = nextFreeFilename (filename)
  212.     needsSave = True
  213.     try:
  214.         os.remove (filename)
  215.     except:
  216.         pass
  217.  
  218.     os.rename (tmp_filename, filename)
  219.  
  220.     return filename
  221.  
  222. # Filter out all non alpha-numeric characters from a future directory name so we 
  223. # can create a corresponding directory on disk without bumping into platform 
  224. # specific pathname limitations.
  225. def filterDirectoryName(name):
  226.     return re.sub(r'[^a-zA-Z0-9]', '-', name)
  227.  
  228.